/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util.actions;
import java.awt.FlowLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.MessageFormat;
import java.io.*;
import java.net.URL;
import java.util.*;
import javax.swing.*;
import org.openide.util.SharedClassObject;
import org.openide.util.HelpCtx;
import org.openide.TopManager;
import org.openide.NotifyDescriptor.Message;
import org.openide.util.NbBundle;
import org.openide.awt.JPopupMenuPlus;
/* not relevant here --jglick
* The actions system allows connection between action
* "invokers" and an action "performer", where in some cases the
* performer of the action can be the action class itself, while in
* other cases it can be a class that implements the ActionPerformer
* interface and is registered at the action via setActionPerformer.
*/
/**
* The common predecessor of callable actions in the IDE.
* <P>
* Also implements the Swing {@link Action} to enable use
* with the Swing action model.
* <p>An action class is a <em>singleton</em>, i.e. should generally contain no instance state.
* Rather, subclassing and use of abstract protected methods should be used
* to create variants of the action.
* <p>While it is possible to subclass this class directly--for example, if your "action"
* is really a placeholder for a popup menu that shows other actions--most people will
* prefer to use one of the subclasses, which are more convenient.
*
* @author Ian Formanek, Jaroslav Tulach
*/
public abstract class SystemAction extends SharedClassObject
implements Action {
/** Name of property indicating whether or not the action is enabled. */
public static final String PROP_ENABLED = "enabled"; // NOI18N
/** Name of property for the action's display icon. */
public static final String PROP_ICON = "icon"; // NOI18N
/** serialVersionUID */
static final long serialVersionUID = 7131978192935797459L;
/** Obtain a singleton instance of the action with a specified class.
* If there already is a instance then it is returned, otherwise
* a new one is created.
*
* @param actionClass the class of the action to find
* @return the singleton action instance
* @exception ClassCastException if the class is not <code>SystemAction</code>
* @exception IllegalArgumentException if the instance cannot be created
*/
public static SystemAction get (Class actionClass) {
return (SystemAction)findObject (actionClass, true);
}
/** Get a human presentable name of the action.
* This may be
* presented as an item in a menu.
* @return the name of the action
*/
public abstract String getName ();
/** Get a help context for the action.
* @return the help context for this action
*/
public abstract HelpCtx getHelpCtx ();
/** Test whether the action is currently enabled.
* @return <code>true</code> if so
*/
public boolean isEnabled() {
return getProperty (PROP_ENABLED).equals (Boolean.TRUE);
}
/** Set whether the action should be enabled.
* @param value <code>true</code> to enable it
*/
public void setEnabled(boolean value) {
putProperty (PROP_ENABLED, value ? Boolean.TRUE : Boolean.FALSE, true);
}
/** Set a property in the singleton. This property is common for all instances
* of the same class.
*
* @param name the name of the property
* @param value the value
*/
public final void putValue (String name, Object value) {
putProperty (name, value, true);
// Could handle putValue (SMALL_ICON, ImageIcon icon) but not
// really that important.
}
/** Get a property in the singleton. Values are shared among all instances of the same class.
* The special tokens {@link Action#NAME} and {@link Action#SMALL_ICON} are also recognized
* and delegated to {@link #getName} and {@link #getIcon}, resp.
* @param name the name of the property
* @return the value
*/
public final Object getValue (String name) {
Object val = getProperty (name);
if (val == null) {
if (NAME.equals (name))
val = getName ();
else if (SMALL_ICON.equals (name))
val = getIcon ();
}
return val;
}
/** Actually perform the action.
* Specified in {@link java.awt.event.ActionListener#actionPerformed}.
* <p>In some cases, the implementation may have an empty body,
* if the presenters handle the performing of the action in a different way
* than by calling this method.
* @param ev the event triggering the action
*/
public abstract void actionPerformed (java.awt.event.ActionEvent ev);
/** Initialize the action.
* The default implementation just enabled it.
*/
protected void initialize () {
putProperty (PROP_ENABLED, Boolean.TRUE);
super.initialize ();
}
/** Indicate whether action state should be cleared after the last action of this class is deleted.
* @return <code>false</code> in the default implementation
*/
protected boolean clearSharedData () {
return false;
}
/** Set the action's display icon.
* @param icon the icon
*/
public final void setIcon (ImageIcon icon) {
putProperty (PROP_ICON, icon, true);
}
/** Get the action's display icon.
* @return the icon
*/
public final ImageIcon getIcon () {
synchronized (getLock ()) {
ImageIcon img = (ImageIcon)getProperty (PROP_ICON);
if (img == null) {
// create the icon from the resource
final String resName = iconResource ();
URL url = resName == null ? null : getClass ().getResource (resName);
if (url == null) {
// TopManager.getDefault ().notify (new Message ("Icon \"" + iconResource () + "\" not found"); // NOI18N
// System.out.println ("Icon for " + getClass ().getName () + " with resource \"" + iconResource () + "\" not found" // NOI18N
throw new IllegalStateException (MessageFormat.format(NbBundle.getBundle(SystemAction.class).getString("MSG_FMT_IconNotFound"), new Object [] {getClass ().getName (), iconResource () }));
}
img = new FixedIcon (url);
putProperty (PROP_ICON, img);
}
return img;
}
}
/** Specify the proper resource name for the action's icon.
* Should be overridden by subclasses.
* Typically this should be a 16x16 color GIF.
* @return the resource name for the icon, e.g. <code>/com/mycom/mymodule/myIcon.gif</code>
*/
protected String iconResource () {
return "/org/openide/resources/actions/empty.gif"; // NOI18N
}
/** Create the default toolbar representation of an array of actions.
* Null items in the array will add a separator to the toolbar.
*
* @param actions actions to show in the generated toolbar
* @return a toolbar instance displaying them
*/
public static JToolBar createToolbarPresenter (SystemAction[] actions) {
JToolBar p = new JToolBar ();
int i, k = actions.length;
for (i = 0; i < k; i++) {
if (actions [i] == null)
p.addSeparator();
else
if (actions [i] instanceof Presenter.Toolbar)
p.add (((Presenter.Toolbar)actions [i]).getToolbarPresenter ());
}
return p;
}
/** Concatenate two arrays of actions.
* @param actions1 first array of actions to link
* @param actions1 second array of actions to link
* @return an array of both sets of actions in the same order
*/
public static SystemAction[] linkActions (SystemAction[] actions1, SystemAction[] actions2) {
List l = new Vector (Arrays.asList (actions1));
l.addAll (Arrays.asList (actions2));
return (SystemAction[]) l.toArray (actions1);
}
/** Create the default popup menu representation of an array of actions.
* @param actions actions to show in the generated menu
* @return a popup menu displaying them
*/
public static JPopupMenu createPopupMenu(SystemAction []actions) {
JPopupMenu popupMenu = new JPopupMenuPlus();
JMenuItem item;
for(int i = 0; i < actions.length; i++) {
if (actions[i] == null) {
popupMenu.addSeparator ();
continue;
}
if (actions[i] instanceof Presenter.Popup) {
item = ((Presenter.Popup)actions[i]).getPopupPresenter ();
} else {
item = new JMenuItem (actions[i].getName ());
item.setEnabled(false);
}
popupMenu.add (item);
}
return popupMenu;
}
/** Icon with fixed size 16x16.
*/
private static final class FixedIcon extends ImageIcon {
public FixedIcon (java.net.URL u) {
super (u);
}
/** Does not wait for the icon because we know the size.
*/
protected void loadImage (java.awt.Image i) {
}
/**
* Draw the icon at the specified location. Icon implementations
* may use the Component argument to get properties useful for
* painting, e.g. the foreground or background color.
*/
public void paintIcon(java.awt.Component c, java.awt.Graphics g, int x, int y) {
// waits till the icon is ok.
super.loadImage (getImage ());
super.paintIcon (c, g, x, y);
}
/**
* Returns the icon's width.
*
* @return an int specifying the fixed width of the icon.
*/
public int getIconWidth() {
return 16;
}
/**
* Returns the icon's height.
*
* @return an int specifying the fixed height of the icon.
*/
public int getIconHeight() {
return 16;
}
}
}
/*
* Log
* 4 Tuborg 1.3 07/29/98 Jaroslav Tulach Does not allow shared data
* to be cleared from memory.
* 3 Tuborg 1.2 07/29/98 Jaroslav Tulach Removed internal field
* because of quick
* initialization.
*
* 2 Tuborg 1.1 06/15/98 Ian Formanek
* 1 Tuborg 1.0 06/11/98 David Peroutka
* $
* Beta Change History:
* 0 Tuborg 0.33 --/--/98 Jaroslav Tulach Removed performAction (we have talk about it and in
* 0 Tuborg 0.33 --/--/98 Jaroslav Tulach BooleanStateAction it is missing) and added firePropertyChange event
* 0 Tuborg 0.34 --/--/98 Jaroslav Tulach Import of org.openide.util.ActionDescription
* 0 Tuborg 0.35 --/--/98 Jaroslav Tulach Minimal type
*/